package space.jevin.jevinboot.config;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjectUtil;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import space.jevin.jevinboot.entity.RequestResult;
import space.jevin.jevinboot.exception.BaseException;
import space.jevin.jevinboot.exception.business.AccessErrorCode;
import space.jevin.jevinboot.exception.system.ErrorCode;
import space.jevin.jevinboot.exception.unknown.UnknownErrorCode;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.beans.PropertyEditorSupport;
import java.lang.reflect.UndeclaredThrowableException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 统一异常处理
 *
 * @author zhangjiehang
 * @version V1.0
 * @date 2021/5/11 11:42 上午
 */
@RestControllerAdvice
@Slf4j
public class ExceptionHandlerAdvice {

    /**
     * 全局异常捕捉处理
     */
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public RequestResult<Object> errorHandler(Exception ex, HttpServletRequest request) {
        RequestResult<Object> result = new RequestResult<>();
        if (UndeclaredThrowableException.class.equals(ex.getClass())) {
            // 处理方法没有抛出异常
            Throwable throwable = ((UndeclaredThrowableException) ex).getUndeclaredThrowable();
            if (BaseException.class.equals(throwable.getClass())) {
                BaseException errorException = (BaseException) throwable;
                result.setCode(errorException.getEnumErrorMessage());
                result.setData("");
                return result;
            }
        }
        log.error("请求接口出错，请求URI:[{}]，出错原因：[{}]", request.getRequestURI(), ex.getMessage(), ex);
        result.setCode(UnknownErrorCode.UNKNOWN);
        return result;
    }


    /**
     * 拦截捕捉自定义异常 ErrorException.class
     */
    @ExceptionHandler(BaseException.class)
    public RequestResult<Object> exErrorHandler(BaseException ex) {
        // TODO: 2021/5/11 暂时在此判断是何种类型的异常码
        String enumType = null;
        if (ex.getEnumErrorMessage().getClass().equals(AccessErrorCode.class)) {
            enumType = AccessErrorCode.class.getTypeName();
        } else
            if (ex.getEnumErrorMessage().getClass().equals(ErrorCode.class)) {
                enumType = ErrorCode.class.getTypeName();
            } else
                if (ex.getEnumErrorMessage().getClass().equals(UnknownErrorCode.class)) {
                    enumType = UnknownErrorCode.class.getTypeName();
                }
        log.info("当前是属于:{}", enumType);
        RequestResult<Object> result = new RequestResult<>();
        result.setCode(ex.getEnumErrorMessage());
        result.setData("");
        return result;
    }

    /**
     * 拦截捕捉参数校验异常 MethodArgumentNotValidException.class
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public RequestResult<Object> exErrorHandler(MethodArgumentNotValidException ex) {
        // 同样是获取BindingResult对象，然后获取其中的错误信息
        // 如果前面开启了fail_fast，事实上这里只会有一个信息
        // 如果没有，则可能又多个
        List<String> errorInformation = ex.getBindingResult().getAllErrors()
                .stream()
                .map(ObjectError::getDefaultMessage)
                .collect(Collectors.toList());
        RequestResult<Object> result = new RequestResult<>();
        result.setData("");
        result.setCode(ErrorCode.PARAM.getCode());
        result.setMessage(errorInformation.toString());
        return result;
    }

    /**
     * 拦截捕捉参数校验异常 ConstraintViolationException.class
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public RequestResult<Object> exErrorHandler(ConstraintViolationException ex) {
        List<String> errorInformation = ex.getConstraintViolations()
                .stream()
                .map(ConstraintViolation::getMessage)
                .collect(Collectors.toList());
        RequestResult<Object> result = new RequestResult<>();
        result.setData("");
        result.setCode(ErrorCode.PARAM.getCode());
        result.setMessage(errorInformation.toString());
        return result;
    }

    /**
     * 参数类型不匹配异常 MethodArgumentTypeMismatchException.class
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public RequestResult<Object> exErrorHandler(MethodArgumentTypeMismatchException ex) {
        RequestResult<Object> result = new RequestResult<>();
        result.setData("");
        result.setCode(ErrorCode.PARAM.getCode());
        result.setMessage(ex.getMessage());
        return result;
    }
}
